D3学习
/ / 点击 / 阅读耗时 15 分钟这篇是D3的学习总结。由于Freecodecamp的D3新版教程还没有上线,D3的基础学习资料是自己从网上搜集来跟着学的,然后完成了Freecodecamp上的5个小项目。
基础学习
这里是D3官方给出的一些教程,我看了Introductions & Core Concepts部分的前10篇,全部是D3的作者Mike Bostock大神写,其中Thinking with Joins以及How Selections Work这两篇看过之后豁然开朗,大概明白了D3为何要写成这样,比如先引用不存在的节点再创造节点。D3做出来的东西真的很惊艳,是我最喜欢的一个库(虽然我也没接触几个…)。basic bar、letter frequencies、general update 1、general update 2、general update 3、circle这6个练习是跟着Mike Bostock大神的教程自己敲进去的代码实现。上述给出的教程链接感觉很重要,日后遗忘可以进行参考。
使用codepen写练习有时需要外部获取数据文件,比如图片、json文件、tsv文件。对于图片,使用google drive和dropbox均可以达到目的,google drive使用gdurl或这个均可以生成永久链接(gdurl有时会生成http形式的链接,在codepen中无法使用)。而json文件、tsv文件存储在google drive中无法使用ajax技术获取,目前的办法是存储在dropbox,生成分享链接,稍作修改,作为最后的url。
正确的url:https://dl.dropboxusercontent.com/s/
+ hfgg7cdoedkv1he/ky-counties.json(dropbox生成的链接中包含这部分,相应替换) +?dl=1
。这里插一段题外话,这三个月的自学让自己发现,那种从非常基础的底层理论入手,全面学习后再进行各项练习的学习方法或许真的不适合自己,当然这也是对特定的学科,特定阶段而言。比如学习语法,就需要全面了解一下常用基础语法,才能顺利进行下一步。但是更重要的进一步的提升需要的是大量的练习,从目的出发,反向学习需要的东西,更有效率。目前发现对于语言(机器语言、自然语言)的学习,这种方法对自己特别有效。但对于写程序而言,这么说似乎有些耍小聪明,毕竟计算机科学是一套完整的理论的体系,还有大量的基础等着我去学习,任重道远。
下面是freecodecamp中的5个项目练习,主要记录自己遇到的一些问题,解决办法,没有解决的问题以及一些感想。
Visualize Data with a Bar Chart
这个项目是利用D3做了一张柱状图,涉及的是D3的基本的标准使用方法。
- 引用的库: d3标准库和d3-tip库
- 过程: 1. 准备阶段:获取json文件的url、数据转换方式、图纸大小、坐标轴的比例和转换;2.画图阶段:利用d3.json获取数据,完善坐标轴标尺,绘制图形、坐标轴、标题等。
遇到的问题:
1.时间数据的转换var parseTime = d3.timeParse("%Y-%m-%d");
var timeFormat = d3.timeFormat(%Y-%m);
利用上面两行代码将以string格式表示的时间数据和js中的时间类型相互转换,更多的转换类型点击这里。可以在使用数据前统一对数据进行预处理,如下。var data = json.data; data.forEach(function(d){ d[0] = parseTime(d[0]); d[1] = +d[1]; });
2.比例尺
这个项目使用了三种比例尺,时间、线性、序数比例尺,对应d3.scaleTime(用来设置横坐标轴)、d3.scaleLinear(用来设置纵坐标轴以及图形的y值定位)以及d3.scaleBand(用来设置图形的宽度以及图形x值定位)。同时利用d3.scaleBand和d3.scaleTime来进行图形x方向定位和宽度设置时,图纸宽度的选择是关键!图纸有效宽度/数据数量 = 一个整数
,否则画出的bar之间会有间距。其实,只是用d3.scaleBand可以实现目的,在后面的项目中有用法。
3.绑定问题
将g绑定到svg后,再绑定数据时,不用使用tag名称,会出现数据绑定不全的问题,此时应使用class名称来绑定。具体原因没有找到说法。
Visualize Data with a Scatterplot Graph
这个项目的图形是用circle表示的,和bar大同小异。
- 遇到的问题
1.在固定的位置用来显示tip:先创建一个固定位置,然后在具体操作代码处稍作调整。
创建固定位置:
调整代码:chart.call(tip); var show = chart.append("g") .attr("id", "show") .attr("cx", "5") .attr("cy", "5");
.on("mouseover", function(d){tip.show(d,document.getElementById("show"))})
Visualize Data with a Heat Map
这个项目用来表示地表温度,画出来有些震撼,基本没有遇到难点。
Show National Contiguity with a Force Directed Graph
这张图使用了d3的force layout。说实话,对force layout的学习并不深入,很多东西还不太明白,此处只记录了force的基本实现过程,其他同上。
- 引用的库:d3标准库
- 参考文档:官方文档、实例1、实例2、sprite
注意:
1.div使用left、right属性来定位
2.为了在svg中使用css-sprite, link和node要放在同一个div内
html:<div id="root" width="900px" height="600px"> <h2>Force Directed Graph of State Contiguity</h2> <div id="node" ></div> </div>
js:
var link = svg.append("g") .attr("class", "links") .selectAll("line") .data(graph.links) .enter().append("line") .attr("stroke-width", 1); var node = d3.select("#node") .selectAll(".flag") .data(graph.nodes) .enter().append("div") .attr("class", function(d){ return "flag flag-" + d.code; })
2.这个练习没有使用d3-tip库来实现tooltip,方法如下
var tip = d3.select("#root") .append("div") .attr("class", "tooltip") .style("opacity", 0); ... .on("mouseover", function(d) { tip.transition() .duration(200) .style("opacity", .9); tip.html(d.country) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY) + "px"); }) .on("mouseout", function(d) { tip.transition() .style("opacity", 0);
3.css的设置,涉及绝对位置显示和居中的问题
svg{ border: 1px black solid; display: block; margin: auto; } #node{ position: absolute; margin:auto; width: 900px; left: 0; right: 0; } div.tooltip { position: absolute; text-align: center; padding: 2px; font: 12px sans-serif; color: white; background: steelblue; border: 0px; border-radius: 8px; pointer-events: none; } .flag { position: absolute; width: 16px; height: 11px; background: url('http://gdurl.com/R0x1') no-repeat; }
过程:
1.创建一个force模拟对象var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function(d){return d.index;})) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(width / 2, height / 2)) .force("y", d3.forceY(0)) .force("x", d3.forceX(0));
2.数据绑定
simulation .nodes(graph.nodes) .on("tick", ticked); simulation.force("link") .links(graph.links);
3.正常创建link和node
var link = svg.append("g") .attr("class", "links") .selectAll("line") .data(graph.links) .enter().append("line") .attr("stroke-width", 1); var node = d3.select("#node") .selectAll(".flag") .data(graph.nodes) .enter().append("div") .attr("class", function(d){ return "flag flag-" + d.code; }) .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)) .on("mouseover", function(d) { tip.transition() .duration(200) .style("opacity", .9); tip.html(d.country) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY) + "px"); }) .on("mouseout", function(d) { tip.transition() .style("opacity", 0);
4.对node和link的操作
function ticked(){ link .attr("x1", function(d){return d.source.x;}) .attr("y1", function(d){return d.source.y;}) .attr("x2", function(d){return d.target.x;}) .attr("y2", function(d){return d.target.y;}); node .style("left", function(d){return d.x + "px";}) .style("top", function(d){return d.y + "px";}); }
Map data across the globe
使用D3来绘制地图,做完这个项目,我意识到D3绘图是一层一层画的,不同的内容位于不同的图层,后画的内容显示在上层。开始做项目之前,先跟着下面的两个教程预热了一下,画了波士顿老鼠分布地图和美国失业情况地图。
- 教程:地图gallery,DUSPviz
- 涉及的库:D3标准库,topojson库,D3 queue库
- 重要概念:1.projection: 自己理解为某种模板,用来处理地理坐标和图纸坐标的关系;2.topojson:用来处理数据的一种方法,主要用了其中的topojson.feature()(用来转换数据)和topojson.mesh()(转换同时筛选数据);3.queue:用于下载完所需的全部数据再执行后续的操作。
- 过程: 1.基本设置;2.画地图;3.添加陨石数据;4;添加zoom
遇到的问题:
1.大圆遮挡了小圆
使用sort方法对数据进行了从大到小的排序,利用后画的图形在上层的特点。data.sort(function(a, b){ if(a.properties.mass > b.properties.mass) return -1; else if(a.properties.mass < b.properties.mass) return 1; return 0; });
2.append数据时,遇到null的情况处理办法
.attr("cx", function(m){return m.geometry == null ? projection([0, 0])[0] : projection(m.geometry.coordinates)[0];}) .attr("cy", function(m){return m.geometry == null ? projection([0, 0])[0] : projection(m.geometry.coordinates)[1];}) .attr("fill", function(m, i){return color(i);}) .attr("r", function(m){return m.geometry == null ? 0 : size(Math.sqrt(m.properties.mass));})
3.使用zoom
var zoom = d3.zoom() .scaleExtent([1, 8]) .on("zoom", zoomed); function zoomed(){ var transform = d3.zoomTransform(this); g.attr("transform", transform); d3.selectAll("circle").attr("transform", transform); } svg.call(zoom);
4.svg的居中
svg border: 1px solid black display: block margin: auto